/*
	OpenGL Frambuffer Object (FBO) abstraction. Includes fp16 framebuffers

	MIT License

	Copyright (c) 2009, Markus Broecker <mbrckr@gmail.com>

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
 */

#ifndef FRAMEBUFFER_INCLUDED
#define FRAMEBUFFER_INCLUDED

#include <stdexcept>

#include <string>

namespace glTools
{

class Framebuffer
{
public:
	
	/// \name Internal type declarations
	/// \{
	
	/// Describes the datatype used in the storage for _all_ textures
	enum DataType
	{
		/// Standard 8Bit per channel
		UNSIGNED_BYTE,
		/// 16Bit (NVidia) Half-precision floats
		HALF_FLOAT,
		/// 32Bit floats
		FLOAT
	};
	
	/// Describes the Number of channels for _all_ textures used
	enum Channels
	{
		INTENSITY,
		RGB,
		RGBA
	};
	
	/// Interpolation used for minification
	enum MinFilter
	{
		/// Nearest interpolation
		NEAREST,
		/// Linear interpolation
		LINEAR,
		/// Create Mipmaps
		MIPMAP
	};
	
	/// \}
	
	/// \name Construction
	/// \{

	/// Constructor
	/**	Creates a new framebuffer object that can be bound. It textures will
		be used as render target for OpenGL writes and they can be bound as
		textures.
		@param width Width of all the textures
		@param height Height of all textures
		@param channels The amount of channels _per texture_
		@param type The datatype for all textures.
		@param colorbufferCount Number of color buffers/textures to create
	*/	
	Framebuffer(unsigned int width, unsigned int height, Channels channels = RGB, DataType type = UNSIGNED_BYTE, unsigned int colorbufferCount = 1, MinFilter filter=LINEAR);
	/// D'Tor
	~Framebuffer();
	
	/// \}
	
	/// \name Write control
	/// \{
		
	/// Binds this framebuffer and sets up OpenGL to write to it
	void bind();
	/// Disables this framebuffer and returns to backbuffer rendering
	void disable();
	
	/// }
	
	/// \name Data access
	/// \{
	
	unsigned int getColorbufferCount() const;
	unsigned int getColorbuffer(unsigned int index) const throw(std::out_of_range);
	
	unsigned int getWidth() const;
	unsigned int getHeight() const;
	
	Channels getChannels() const;
	DataType getDataType() const;
	MinFilter getMinFilterType() const;
	
	/// \}
	
private:
	unsigned int 	mWidth, mHeight;
	unsigned int	mBuffer;
	
	Channels		mChannels;
	DataType		mType;
	MinFilter		mFilter;
	
	unsigned int* 	mColorbuffer;
	unsigned int	mColorbufferCount;
	
	unsigned int	mDepthRenderbuffer;
	
};

class Depthbuffer
{
public:
	Depthbuffer(unsigned int width, unsigned int height);
	~Depthbuffer();
	
	/// \name Write control
	/// \{
	/// Binds this depthbuffer for writing
	void bind();
	/// Returns to regular rendering
	void disable();
	/// \}
	
	/// \name Member access
	/// \{
	unsigned int getBuffer() const;
	
	unsigned int getWidth() const;
	unsigned int getHeight() const;
	/// \}

private:
	unsigned int	mBuffer;
	unsigned int	mDepthTexture;
	unsigned int	mWidth, mHeight;
	
};



inline unsigned int Framebuffer::getColorbufferCount() const
{
	return mColorbufferCount;
}

inline unsigned int Framebuffer::getColorbuffer(unsigned int i) const throw(std::out_of_range)
{
	if (i >= mColorbufferCount)
		throw std::out_of_range("Index exceeds number of color buffers");
	
	return mColorbuffer[i];
}

inline unsigned int Framebuffer::getWidth() const
{
	return mWidth;
}

inline unsigned int Framebuffer::getHeight() const
{
	return mHeight;
}

inline Framebuffer::Channels Framebuffer::getChannels() const
{
	return mChannels;
}

inline Framebuffer::DataType Framebuffer::getDataType() const
{
	return mType;
}

inline Framebuffer::MinFilter Framebuffer::getMinFilterType() const
{
	return mFilter;
}

inline unsigned int Depthbuffer::getBuffer() const
{
	return mDepthTexture;
}

inline unsigned int Depthbuffer::getWidth() const
{
	return mWidth;
}

inline unsigned int Depthbuffer::getHeight() const
{
	return mHeight;
}

};

#endif
